using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using Voodoo.Utils.Ease;

namespace Voodoo.Meta
{
	public class LeaderboardLayoutAnimation
	{
		private const int _firstPlaceDuration = 1500;

		private const int _delayBetweenAnimPhase = 300;

		private const float _rankStepPower = 1.3f;

		private const Ease _easeType = Ease.EaseOutQuad;

		private readonly LeaderboardLayoutUI _layoutUI;

		private readonly LeaderboardAnimationData _leaderboardAnimationData;

		private readonly float _margin;

		private readonly LeaderboardPlayerSlot _playerSlot;

		public event Action<int> rankProgressionProcessed;

		public event Action rankUpdated;

		public LeaderboardLayoutAnimation(LeaderboardLayoutUI layoutUI, LeaderboardAnimationData leaderboardAnimationData, float margin)
		{
			_layoutUI = layoutUI;
			_leaderboardAnimationData = leaderboardAnimationData;
			_margin = margin;
			_playerSlot = _layoutUI.PlayerSlot;
		}

		public async Task LaunchAnimationAsync(CancellationToken cancelToken = default(CancellationToken))
		{
			if (10000 <= _layoutUI.PlayerSlot.Rank || _layoutUI.PlayerSlot.Rank <= 1)
			{
				await IdleAnimationAsync(cancelToken);
				return;
			}
			_layoutUI.MovePlayerSlotInLayoutTransform();
			await AnimationAsync(cancelToken);
		}

		private async Task IdleAnimationAsync(CancellationToken cancelToken)
		{
			await Task.Delay(1500, cancelToken);
		}

		private async Task AnimationAsync(CancellationToken cancelToken = default(CancellationToken))
		{
			await ScalePlayerSlotAsync(Vector3.one * _leaderboardAnimationData.playerUpScaleFactor, _leaderboardAnimationData.playerUpScaleDuration, cancelToken);
			await ProcessSlotsAndScoreIncreaseAsync(cancelToken);
			await Task.Delay(300, cancelToken);
			_layoutUI.MoveLastSlots();
			await Task.Yield();
			_layoutUI.SwitchLastSlotIndex();
			await ScalePlayerSlotAsync(Vector3.one, _leaderboardAnimationData.playerDownScaleDuration, cancelToken);
			await Task.Delay(300, cancelToken);
			this.rankUpdated?.Invoke();
			_layoutUI.RefreshPlayerRank();
			await Task.Delay(_leaderboardAnimationData.WaitingTimeInMilliSeconds, cancelToken);
		}

		private async Task MoveAnchoredPositionAsync(LeaderboardPlayerSlot slot, Vector2 finalAnchoredPosition, float duration, CancellationToken cancelToken = default(CancellationToken))
		{
			Vector2 initialAnchoredPosition = slot.AnchoredPosition;
			float timeElapsed = 0f;
			while (timeElapsed < duration)
			{
				slot.AnchoredPosition = Vector2.Lerp(initialAnchoredPosition, finalAnchoredPosition, timeElapsed / duration);
				await Task.Yield();
				timeElapsed += Time.deltaTime;
				cancelToken.ThrowIfCancellationRequested();
			}
			slot.AnchoredPosition = finalAnchoredPosition;
		}

		private async Task ScalePlayerSlotAsync(Vector3 finalScale, float duration, CancellationToken cancelToken = default(CancellationToken))
		{
			Vector3 initialScale = _playerSlot.transform.localScale;
			float timeElapsed = 0f;
			while (timeElapsed < duration)
			{
				_playerSlot.transform.localScale = Vector3.Lerp(initialScale, finalScale, timeElapsed / duration);
				await Task.Yield();
				timeElapsed += Time.deltaTime;
				cancelToken.ThrowIfCancellationRequested();
			}
			_playerSlot.transform.localScale = finalScale;
		}

		private async Task ProcessSlotsAndScoreIncreaseAsync(CancellationToken cancelToken = default(CancellationToken))
		{
			float duration = _leaderboardAnimationData.duration;
			LeaderboardRankLogic rankLogic = _layoutUI.RankLogic;
			int rankStep = rankLogic.RankStep;
			IReadOnlyList<LeaderboardPlayerSlot> slots = _layoutUI.Slots;
			CalculationEasing.Function easingFunction = CalculationEasing.GetEasingFunction(Ease.EaseOutQuad);
			float elapsedTime = 0f;
			float currentEaseDuration = 0f;
			int directionMultiplier = ((!rankLogic.RankUp) ? 1 : (-1));
			for (int i = 0; i < rankLogic.RankStep; i++)
			{
				this.rankProgressionProcessed?.Invoke(i);
				float t = easingFunction(0f, 1f, (float)i / (float)rankStep);
				int score = Mathf.RoundToInt(Mathf.Lerp(rankLogic.PreviousScore, rankLogic.NewScore, t));
				_playerSlot.UpdateScore(score);
				float num = (duration - elapsedTime) / Mathf.Pow(rankStep - i, 1.3f);
				currentEaseDuration += num;
				if (elapsedTime <= currentEaseDuration)
				{
					List<Task> list = new List<Task>();
					for (int j = 0; j < slots.Count; j++)
					{
						Vector2 finalAnchoredPosition = slots[j].AnchoredPosition + new Vector2(0f, slots[j].Height + _margin) * directionMultiplier;
						Task item = MoveAnchoredPositionAsync(slots[j], finalAnchoredPosition, num, cancelToken);
						list.Add(item);
					}
					float currentTime = Time.realtimeSinceStartup;
					await Task.WhenAll(list);
					float num2 = Time.realtimeSinceStartup - currentTime;
					elapsedTime += num2;
				}
				else
				{
					for (int k = 0; k < slots.Count; k++)
					{
						slots[k].MoveSlot(_margin, directionMultiplier);
					}
				}
				_layoutUI.SwitchLastSlotIndex();
				cancelToken.ThrowIfCancellationRequested();
			}
			_playerSlot.UpdateScore(rankLogic.NewScore);
		}
	}
}
